package com.myf.services;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.myf.AppException;
import com.myf.PaginationResult;
import com.myf.Utility;
import com.myf.dtos.LoginCredential;
import com.myf.dtos.user.UserInput;
import com.myf.dtos.user.UserQueryInput;
import com.myf.dtos.user.UserQueryOutput;
import com.myf.dtos.user.UserUpdatePassword;
import com.myf.entities.UserEntity;
import com.myf.mappers.UserMapper;
import org.springframework.stereotype.Service;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;


@Service
public class UserService {
    private final UserMapper userMapper;

    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public List<UserEntity> getAllUsers() {
        return userMapper.findAll();
    }

    public Optional<UserEntity> getByCredential(String account, String password) {
        return userMapper.getByCredential(account, password);
    }

    static final String defaultPassword = "vXg9dY/B6gcNJH1eQ7Bl2g==";

    public UserEntity getById(int id) {
        return userMapper.selectById(id);
    }

    public UserEntity login(LoginCredential credential, String lastLoginIP) throws AppException, JsonProcessingException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        if (credential.account.isBlank() || credential.password.isBlank()) {
            throw new AppException("invalid credentials");
        }
        var user = userMapper.getByCredential(credential.account, Utility.encrypt(credential.password));
        if (user.isEmpty()) {
            throw new AppException("invalid credentials");
        }
        var userEntity = user.get();
        // 更新最后登录时间
        var map = getStringObjectHashMap(credential, lastLoginIP, userEntity);
        userMapper.updateLastLogin(map);
        return userEntity;
    }

    private static HashMap<String, Object> getStringObjectHashMap(LoginCredential credential, String lastLoginIP, UserEntity userEntity) throws JsonProcessingException {
        var mapper = new ObjectMapper();
        var jNode = (ObjectNode) mapper.readTree(userEntity.profile);
        jNode.put("theme", credential.theme);
        jNode.put("language", credential.language);
        jNode.put("timezone", "Asia/Shanghai");
        var current = Utility.getCurrentUTCTime();
        var map = new HashMap<String, Object>();
        map.put("id", userEntity.id);
        map.put("profile", jNode.toString());
        map.put("lastLoginIP", lastLoginIP);
        map.put("lastLoginAt", current);
        return map;
    }

    public int save(UserInput userInput, int currentUserId) throws JsonProcessingException, AppException {
        if (userInput.id == null || userInput.id == 0) {
            // 检查名称是否重复
            var map = new HashMap<String, Object>();
            map.put("account", userInput.account);
            if (userMapper.isAccountExists(map) == 1) {
                throw new AppException("account ".concat(userInput.account).concat(" already exists"));
            }
            // 插入数据库
            var userEntity = new UserEntity();
            userEntity.account = userInput.account;
            userEntity.password = defaultPassword;
            setToEntity(userInput, userEntity);
            var mapper = new ObjectMapper();
            var jNode = (ObjectNode) mapper.readTree("{}");
            jNode.put("timezone", userInput.timezone);
            userEntity.profile = jNode.toString();
            userEntity.createAt = Utility.getCurrentUTCTime();
            userEntity.createBy = currentUserId;
            userEntity.version = 0;
            return userMapper.insert(userEntity);
        } else {
            // 检查名称是否重复
            var map = new HashMap<String, Object>();
            map.put("id", userInput.id);
            map.put("account", userInput.account);
            if (userMapper.isAccountExists(map) == 1) {
                throw new AppException("account ".concat(userInput.account).concat(" already exists"));
            }
            // 更新数据库
            var userEntity = this.userMapper.selectById(userInput.id);
            if (userEntity == null) {
                throw new AppException("the userinfo is invalid");
            }
            setToEntity(userInput, userEntity);
            var mapper = new ObjectMapper();
            var jNode = (ObjectNode) mapper.readTree(userEntity.profile);
            jNode.put("timezone", userInput.timezone);
            userEntity.profile = jNode.toString();
            userEntity.updateAt = Utility.getCurrentUTCTime();
            userEntity.updateBy = currentUserId;
            return userMapper.updateById(userEntity);
        }
    }

    /// 删除
    public int delete(List<Integer> userIds, int currentUserId) {
        var hashMap = new HashMap<String, Object>();
        hashMap.put("ids", userIds);
        hashMap.put("deleteBy", currentUserId);
        hashMap.put("deleteAt", Utility.getCurrentUTCTime());
        return userMapper.removeByIds(hashMap);
    }

    private void setToEntity(UserInput userInput, UserEntity userEntity) {
        userEntity.name = userInput.name;
        userEntity.code = userInput.code;
//        userEntity.password = userInput.password;
        userEntity.gender = userInput.gender;
        userEntity.email = userInput.email;
        userEntity.birthday = userInput.birthday;
        userEntity.status = userInput.status;
    }

    public int updateLastLogin(Map<String, Object> map) {
        return userMapper.updateLastLogin(map);
    }

    public PaginationResult<UserQueryOutput> queryUsers(UserQueryInput userQueryInput) {
        var map = new HashMap<String, Object>();
        if (!userQueryInput.keyword.isBlank()) {
            map.put("keyword", "%".concat(userQueryInput.keyword).concat("%"));
        }
        map.put("limit", userQueryInput.pageSize);
        map.put("offset", userQueryInput.getOffset());
        var total = userMapper.getPaginationCount(map);
        var rows = userMapper.getPaginationList(map);
        return new PaginationResult<>(total, rows);
    }

    public int updatePassword(UserUpdatePassword input, int currentUserId) throws AppException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        if (input.id == 0 || input.originalPassword.isBlank() || input.newPassword.isBlank()) {
            throw new AppException("the arguments is invalid");
        }
        var user = userMapper.selectById(input.id);
        if (user == null || user.deleteAt != null) {
            throw new AppException("the user is invalid");
        }
        var password = Utility.encrypt(input.originalPassword);
        if (!Objects.equals(password, user.password)) {
            throw new AppException("the orignal password of user is wroing");
        }
        var map = new HashMap<String, Object>();
        map.put("id", input.id);
        map.put("password", Utility.encrypt(input.newPassword));
        map.put("updateAt", Utility.getCurrentUTCTime());
        map.put("updateBy", currentUserId);
        return userMapper.updatePassword(map);
    }

    public int resetPassword(int userId, int currentUserId) {
        var map = new HashMap<String, Object>();
        map.put("id", userId);
        map.put("password", defaultPassword);
        map.put("updateAt", Utility.getCurrentUTCTime());
        map.put("updateBy", currentUserId);
        return userMapper.updatePassword(map);
    }
}
